/***************************************************************************
 *   Copyright (C) 2009 - 2010 by Simon Qian <SimonQian@SimonQian.com>     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "vsf.h"
#include "usrapp.h"
#include "vsfvm_objdump.h"

#define VSFVM_SHELL_PROMPT			">>>"

struct usrapp_param_t
{
	char *src;
} static const usrapp_param =
{
	.src = "\
print(\"Setting environment...\");\r\n\
var led0 = gpio_create(1, 14).config(GPIO_OUTPP).set();   // PB14\r\n\
var led1 = gpio_create(1, 15).config(GPIO_OUTPP).set();   // PB15\r\n\
var led2 = gpio_create(1, 16).config(GPIO_OUTPP).set();   // PB16\r\n\
var led3 = gpio_create(1, 17).config(GPIO_OUTPP).set();   // PB17\r\n\
var led4 = gpio_create(1, 18).config(GPIO_OUTPP).set();   // PB18\r\n\
var ledarr = array_create(1, 0, 5).set(0, led0, led1, led2, led3, led4);\r\n\
print(\"ready.\\r\\nEnter User Mode...\\r\\n\");\r\n\
",
};

static const char *vsfvmc_errcode_str[] =
{
	TO_STR(VSFVMC_ERRCODE_NONE),

	// common error
	TO_STR(VSFVMC_BUG),
	TO_STR(VSFVMC_BYTECODE_TOOLONG),
	TO_STR(VSFVMC_NOT_ENOUGH_RESOURCES),
	TO_STR(VSFVMC_FATAL_ERROR),
	TO_STR(VSFVMC_NOT_SUPPORT),

	// lexer error
	TO_STR(VSFVMC_LEXER_NOT_SUPPORT),
	TO_STR(VSFVMC_LEXER_INVALID_OP),
	TO_STR(VSFVMC_LEXER_INVALID_STRING),
	TO_STR(VSFVMC_LEXER_INVALID_ESCAPE),
	TO_STR(VSFVMC_LEXER_SYMBOL_TOO_LONG),

	// parser error
	TO_STR(VSFVMC_PARSER_UNEXPECTED_TOKEN),
	TO_STR(VSFVMC_PARSER_ALREADY_DEFINED),
	TO_STR(VSFVMC_PARSER_INVALID_CLOSURE),
	TO_STR(VSFVMC_PARSER_INVALID_EXPR),
	TO_STR(VSFVMC_PARSER_UNINITED_CONST),
	TO_STR(VSFVMC_PARSER_INVALID_CONST),
	TO_STR(VSFVMC_PARSER_DIV0),
	TO_STR(VSFVMC_PARSER_EXPECT_FUNC_PARAM),
	TO_STR(VSFVMC_PARSER_TOO_MANY_FUNC_PARAM),
	TO_STR(VSFVMC_PARSER_MEMFUNC_NOT_FOUND),

	// compiler error
	TO_STR(VSFVMC_COMPILER_INVALID_MODULE),
	TO_STR(VSFVMC_COMPILER_INVALID_FUNC),
	TO_STR(VSFVMC_COMPILER_INVALID_FUNC_PARAM),
	TO_STR(VSFVMC_COMPILER_FAIL_USRLIB),
};

static int usrapp_vm_set_bytecode(void *param, uint32_t code, uint32_t pos);
static void usrapp_on_shell_in(void *param);
struct usrapp_t usrapp =
{
	.debug.uart_stream =
	{
		.index = DEBUG_UART_INDEX,
		.mode = VSFHAL_USART_STOPBITS_1 | VSFHAL_USART_PARITY_NONE,
		.int_priority = 0xFF,
		.baudrate = 115200,
		.stream_tx = &usrapp.debug.stream_tx.stream,
		.stream_rx = &usrapp.debug.stream_rx.stream,
	},

	.debug.stream_tx =
	{
		.stream.op = &vsf_fifostream_op,
		.mem.buffer.buffer = (uint8_t *)&usrapp.debug.txbuff,
		.mem.buffer.size = sizeof(usrapp.debug.txbuff),
	},
	.debug.stream_rx =
	{
		.stream.op = &vsf_fifostream_op,
		.stream.callback_rx.param = &usrapp,
		.stream.callback_rx.on_inout = usrapp_on_shell_in,
		.mem.buffer.buffer = (uint8_t *)&usrapp.debug.rxbuff,
		.mem.buffer.size = sizeof(usrapp.debug.rxbuff),
	},

	.vsfvm.runtime.vm.thread_pool.pool_size	= 16,
	.vsfvm.compiler.dart.op					= &vsfvmc_lexer_op_dart,
};

void usrapp_srt_init(struct usrapp_t *app)
{
	VSFSTREAM_INIT(&app->debug.stream_rx);
	VSFSTREAM_INIT(&app->debug.stream_tx);
	vsf_usart_stream_init(&app->debug.uart_stream);
	vsfdbg_init((struct vsf_stream_t *)&app->debug.stream_tx);
}

static int usrapp_vm_set_bytecode(void *param, uint32_t code, uint32_t pos)
{
	struct usrapp_t *app = (struct usrapp_t *)param;
	if (pos >= dimof(app->vsfvm.token))
		return -1;

	app->vsfvm.token[pos] = code;
	return 0;
}

static void usrapp_on_shell_in(void *param)
{
	struct usrapp_t *app = (struct usrapp_t *)param;
	struct vsf_stream_t *stream = &app->debug.stream_rx.stream;
	char ch, *line = app->debug.linebuf;
	struct vsf_buffer_t buffer = { (uint8_t *)&ch, 1 };

	struct vsfvmc_t *vmc = &app->vsfvm.compiler.vmc;
	struct vsfvm_t *vm = &app->vsfvm.runtime.vm;
	struct vsfvm_script_t *script = &app->vsfvm.runtime.script;

	bool newline = false;
	uint32_t *tkbuff = (uint32_t *)script->token;
	uint32_t tkpos;
	int err;

	while (vsfstream_read(stream, &buffer) == 1)
	{
		if (app->debug.linepos >= sizeof(app->debug.linebuf) - 1)
		{
			vsfdbg_printf(VSFCFG_DEBUG_LINEEND "line too long" VSFCFG_DEBUG_LINEEND);
			return;
		}

		switch (ch)
		{
		case '\b':
			if (app->debug.linepos)
			{
				app->debug.linepos--;

				buffer.buffer = "\b \b";
				buffer.size = sizeof("\b \b") - 1;
				vsfstream_write(&app->debug.stream_tx.stream, &buffer);
			}
			break;
		case '\r':
			buffer.buffer = VSFCFG_DEBUG_LINEEND;
			buffer.size = sizeof(VSFCFG_DEBUG_LINEEND);
			vsfstream_write(&app->debug.stream_tx.stream, &buffer);
			goto got_char;
		default:
			vsfstream_write(&app->debug.stream_tx.stream, &buffer);
		got_char:
			line[app->debug.linepos++] = ch;
			if ((ch == '\r') || (ch == '\n'))
			{
				newline = 1;
				break;
			}
			break;
		}
	}
	if (!newline) return;

	line[app->debug.linepos] = '\0';
	app->debug.linepos = 0;

	err = vsfvmc_input(vmc, line);
	if (err < 0)
	{
		err = -err;
		vsfdbg_printf("command line compile error: %s\n",
			(err >= VSFVMC_ERRCODE_END) ? "unknwon error" :
			vsfvmc_errcode_str[err]);
		if (vsfvmc_snapshot_restore(vmc, &app->vsfvm.snapshot) < 0)
		{
			vsfdbg_printf("fail to restore snapshot.\nexiting...\n");
		exit_vm:
			vsfvm_script_fini(vm, script);
			vsfvm_fini(vm);
			vsfvmc_fini(vmc);
			exit(0);
		}
		vsfdbg_prints(VSFVM_SHELL_PROMPT);
		return;
	}
	else if (err > 0)
		return;
	else
	{
		vsfvmc_snapshot_free(&app->vsfvm.snapshot);
		if (vsfvmc_snapshot_take(vmc, &app->vsfvm.snapshot) < 0)
		{
			vsfdbg_printf("fail to take snapshot.\nexiting...\n");
			goto exit_vm;
		}

		// 1. check if unprocessed anchor
		if ((vmc->script.cur_func.curctx.etoken.token > 0) ||
			(vmc->script.cur_func.ctx.sp > 0) ||
			(vmc->script.func_stack.sp > 1) ||
			(app->vsfvm.bytecode_pos == vmc->bytecode_pos))
		{
			return;
		}

		// 2. save bytecode_pos
		tkpos = app->vsfvm.bytecode_pos = vmc->bytecode_pos;
		// 3. append breakpoint
		tkbuff[tkpos] = VSFVM_KEYWORD(VSFVM_CODE_KEYWORD_breakpoint, 0, 0);
		// 4. wake all threads
		script->token_num = tkpos + 1;
		vsflist_foreach(thread, script->thread.next, struct vsfvm_thread_t, list)
			vsfvm_thread_ready(thread);
		// 5. run till breakpoint
		app->vsfvm.wait_prompt = true;
	}
}

void usrapp_srt_poll(struct usrapp_t *app){}
void usrapp_nrt_init(struct usrapp_t *app)
{
	struct vsfvmc_t *vmc = &app->vsfvm.compiler.vmc;
	struct vsfvm_t *vm = &app->vsfvm.runtime.vm;
	struct vsfvm_script_t *script = &app->vsfvm.runtime.script;
	int err;

	vsfvm_ext_register_std();
	vsfvm_ext_register_vsf();

	vsfdbg_prints("environment code:" VSFCFG_DEBUG_LINEEND);
	vsfdbg_prints(usrapp_param.src);
	vsfdbg_prints(VSFCFG_DEBUG_LINEEND);

	vsfvmc_init(vmc, &usrapp, NULL, usrapp_vm_set_bytecode);
	vsfvmc_register_lexer(vmc, &app->vsfvm.compiler.dart);
	vsfvmc_script(vmc, "shell.dart");
	err = vsfvmc_input(vmc, usrapp_param.src);
	if (err < 0)
	{
		err = -err;
		vsfdbg_printf("command line compile error: %s" VSFCFG_DEBUG_LINEEND,
			(err >= VSFVMC_ERRCODE_END) ? "unknwon error" : vsfvmc_errcode_str[err]);
		vsfdbg_printf("compile error around line %d column %d" VSFCFG_DEBUG_LINEEND,
			vmc->script.lexer.curctx.line + 1, vmc->script.lexer.curctx.col + 1);
		vsfvmc_fini(vmc);
		return;
	}
	else if (err > 0)
	{
		vsfdbg_printf("invalid environment code" VSFCFG_DEBUG_LINEEND);
	}

	app->vsfvm.token[vmc->bytecode_pos] = VSFVM_KEYWORD(VSFVM_CODE_KEYWORD_breakpoint, 0, 0);
	script->token = app->vsfvm.token;
	script->token_num = vmc->bytecode_pos + 1;
	vsfvm_init(vm);
	vsfvm_script_init(vm, script);

	vsfvmc_snapshot_take(vmc, &app->vsfvm.snapshot);
	vsfstream_connect_rx(&app->debug.stream_rx.stream);
	app->vsfvm.wait_prompt = true;
}

void usrapp_nrt_poll(struct usrapp_t *app)
{
	struct vsfvm_t *vm = &app->vsfvm.runtime.vm;
	struct vsfvm_script_t *rt_script = &app->vsfvm.runtime.script;
	struct vsfvm_thread_t *thread = vsflist_get_container(rt_script->thread.next,
		struct vsfvm_thread_t, list);
	int err;

	while (1)
	{
		err = vsfvm_poll(vm);
		if (err < 0)
		{
			vsfdbg_printf("vsfvm_poll failed with %d\n", err);
			break;
		}
		else if (!err)
		{
			if (app->vsfvm.wait_prompt &&
				(thread->func.type == VSFVM_CODE_FUNCTION_SCRIPT) &&
				(thread->func.pc == (rt_script->token_num - 1)))
			{
				vsfdbg_prints(VSFVM_SHELL_PROMPT);
				app->vsfvm.wait_prompt = false;
			}
			break;
		}
	}
}

bool usrapp_cansleep(struct usrapp_t *app)
{
	return app->vsfvm.runtime.vm.appendlist.next == NULL;
}
